#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XDL.Framework.Input;
#endregion

namespace XDL.Framework.Screens
{
    /// <summary>
    /// Base class for screens that contain a menu of options. The user can
    /// move up and down to select an entry, or cancel to back out of the screen.
    /// </summary>
    public abstract class MenuScreen : 
        GameScreen
    {
        #region Fields

        List<UIElement> m_controls = new List<UIElement>();
        int m_selectedEntry = -1000;
        AudioManager m_audioManager;
        Color m_selectedColor = Color.Orange;
        Color m_textColor = Color.White;
        #endregion

        #region Properties

        /// <summary>
        /// Gets the list of controls (menu entries, text,...), so derived classes can add
        /// or change the menu contents.
        /// </summary>
        public IList<UIElement> Controls
        {
            get { return m_controls; }
        }

        /// <summary>
        /// Returns the selected menu index (in <see cref="MenuEntries"/> list).
        /// </summary>
        protected int SelectedEntry
        {
            get { return m_selectedEntry; }
        }

        /// <summary>
        /// Gets/sets the selected text color.
        /// </summary>
        public Color SelectedTextColor
        {
            get { return m_selectedColor; }
            set { m_selectedColor = value; }
        }

        /// <summary>
        /// Gets/sets the text color.
        /// </summary>
        public Color TextColor
        {
            get { return m_textColor; }
            set { m_textColor = value; }
        }
        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public MenuScreen()
        {
            TransitionOnTime = TimeSpan.FromSeconds(1.0);
            TransitionOffTime = TimeSpan.FromSeconds(1.0);

        }

        /// <summary>
        /// Load all content.
        /// </summary>
        public override void LoadContent()
        {
            // retrieve the audio manager, done here in lieu of Initialize
            m_audioManager = ScreenManager.GetService<AudioManager>();
            foreach (UIElement it in m_controls)
                it.LoadContent();
            base.LoadContent();
        }

        #endregion

        bool SelectPreviousEntry()
        {
            m_selectedEntry--;

            if ( m_selectedEntry < 0)
                m_selectedEntry = m_controls.Count - 1;

            for (int i = m_selectedEntry; i >= 0; i--)
			{
                if (m_controls[i].Focus())
                {
                    m_selectedEntry = i;
                    return true;
                }
			}

            // we went all the way up and found no selectable entries!!
            m_selectedEntry = -1;
            return false;
        }

        bool SelectNextEntry()
        {
            m_selectedEntry ++;

            if (m_selectedEntry >= m_controls.Count)
                m_selectedEntry = 0;

            if ( m_selectedEntry < 0 )
                m_selectedEntry = 0;

            for(int i=m_selectedEntry;i<m_controls.Count;i++)
            {
                if (m_controls[i].Focus())
                {
                    m_selectedEntry = i;
                    return true;
                }
            }

            // we went all the way down and found no selectable entries!!
            m_selectedEntry = -1;
            return false;
        }

        #region Handle Input
        /// <summary>
        /// Responds to user input, changing the selected entry and accepting
        /// or cancelling the menu.
        /// </summary>
        public override void HandleInput(GameTime gameTime, InputState input)
        {
            if (input.MenuUp)
            {
                // Move to the previous menu entry?
                if (SelectPreviousEntry())
                    if (m_audioManager!=null) m_audioManager.PlayCue("MenuMove");
            }
            else if (input.MenuDown)
            {
                // Move to the next menu entry?
                if (SelectNextEntry())
                    if (m_audioManager != null) m_audioManager.PlayCue("MenuMove");
            }

            // delegate input management
            if ( m_selectedEntry >= 0 && m_selectedEntry < m_controls.Count )
                m_controls[m_selectedEntry].HandleInput(input);

            // close the menu?
            if (input.MenuCancel)
            {
                OnCancel();
            }
        }

        /// <summary>
        /// Handler for when the user has cancelled the menu.
        /// </summary>
        protected virtual void OnCancel()
        {
            if (m_audioManager != null) m_audioManager.PlayCue("MenuClose");

            ExitScreen();
        }


        /// <summary>
        /// Helper overload makes it easy to use OnCancel as a MenuItem event handler.
        /// </summary>
        protected void OnCancel(object sender, EventArgs e)
        {
            OnCancel();
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Updates the menu.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

            // if selection is empty, pick up the first menu entry
            if (m_selectedEntry < 0)
                SelectNextEntry();

            // Update each nested MenuItem object.
            for (int i = 0; i < m_controls.Count; i++)
            {
                bool isSelected = IsActive && (i == m_selectedEntry);

                m_controls[i].Update(this, isSelected, gameTime);
            }
        }

        /// <summary>
        /// Defines the drawing area.
        /// </summary>
        /// <returns></returns>
        public virtual Rectangle GetDrawArea()
        { 
            Viewport viewport = GameServices.Viewport;
            return new Rectangle(viewport.TitleSafeArea.X, viewport.TitleSafeArea.Y, viewport.TitleSafeArea.Width, viewport.TitleSafeArea.Height);
        }

        /// <summary>
        /// Draws the menu.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
            Rectangle boundingBox = GetDrawArea();

            // top/left corner
            Vector2 position = new Vector2(boundingBox.X, boundingBox.Y);

            /*
            // Make the menu slide into place during transitions, using a
            // power curve to make things look more interesting (this makes
            // the movement slow down as it nears the end).
            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
            if (ScreenState == ScreenState.TransitionOn)
                position.Y += transitionOffset * 256;
            else
                position.Y += transitionOffset * 512;
            */

            // Draw each menu entry in turn.
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend,SamplerState.PointClamp,DepthStencilState.Default,RasterizerState.CullNone);

            Vector2[] menuSizes = new Vector2[m_controls.Count];
            float maxLength = float.MinValue;
            for (int i = 0; i < m_controls.Count; i++)
            {
                // get text size
                menuSizes[i] = m_controls[i].Measure();
                if (menuSizes[i].X > maxLength)
                    maxLength = menuSizes[i].X;
            }

            for (int i = 0; i < m_controls.Count; i++)
            {
                // Draw UI element
                m_controls[i].Draw(this, position, IsActive && (i == m_selectedEntry), gameTime);

                // next!
                position.Y += menuSizes[i].Y;
            }

            spriteBatch.End();


            //
            base.Draw(gameTime);
        }


        #endregion
    }
}
